import { getDataType } from '../utils';

function createTextNode(text) {
    return {
        type: 'TEXT_ELEMENT',
        props: {
            nodeValue: text,
            children: [],
        },
    };
}

function createElement(type, props, ...children) {
    return {
        type,
        props: {
            ...props,
            children: children.map(child => {
                // 当子元素是字符串或数字时都为文本节点
                const isProps = typeof child === 'string' || typeof child === 'number';
                return isProps ? createTextNode(child) : child;
            }),
        },
    };
}

function render(element, container) {
    nextUnitOfWork = {
        dom: container,
        props: {
            children: [element],
        },
    };
    root = nextUnitOfWork;
}

function createDom(type) {
    return type === 'TEXT_ELEMENT' ? document.createTextNode('') : document.createElement(type);
}

function updateProps(dom, props) {
    Object.keys(props).forEach(name => {
        // 除了 children 属性，其他属性都直接设置到 dom 上
        if (name !== 'children') {
            dom[name] = props[name];
        }
    });
}

function initChildren(fiber, children) {
    let prevSibling = null;
    // 拿到当前节点的所有子节点
    children.forEach((child, index) => {
        // 为每个子节点创建一个 fiber
        const newFiber = {
            type: child.type,
            props: child.props,
            parent: fiber,
            dom: null,
            child: null,
            sibling: null,
        };
        // 将新创建的 fiber 添加到 fiber 链表中
        if (index === 0) {
            fiber.child = newFiber;
        } else {
            prevSibling.sibling = newFiber;
        }
        prevSibling = newFiber;
    });
}

function updateFunctionComponent(fiber) {
    const children = [fiber.type(fiber.props)];
    initChildren(fiber, children);
}

function updateHostComponent(fiber) {
    if (!fiber.dom) {
        fiber.dom = createDom(fiber.type);
    }
    updateProps(fiber.dom, fiber.props);

    initChildren(fiber, fiber.props.children);
}

function performanceWorkUnit(fiber) {
    const isFunctionComponent = getDataType(fiber.type) === 'Function';
    if (isFunctionComponent) {
        updateFunctionComponent(fiber);
    } else {
        updateHostComponent(fiber);
    }

    if (fiber.child) {
        return fiber.child;
    }
    if (fiber.sibling) {
        return fiber.sibling;
    }

    // 如果当前 fiber 没有子节点，就找到父节点的兄弟节点
    // 直到找到有兄弟节点的父节点，或者找到根节点
    let nextFiber = fiber;
    while (nextFiber) {
        if (nextFiber.sibling) {
            return nextFiber.sibling;
        }
        nextFiber = nextFiber.parent;
    }
}
let root = null;
let nextUnitOfWork = null;
function workLoop(deadline) {
    let shouldYield = false;
    while (!shouldYield && nextUnitOfWork) {
        nextUnitOfWork = performanceWorkUnit(nextUnitOfWork);
        shouldYield = deadline.timeRemaining() < 1;
    }

    if (!nextUnitOfWork && root) {
        commitRoot();
    }
    requestIdleCallback(workLoop);
}

function commitRoot() {
    commitWork(root.child);
    root = null;
}

function commitWork(fiber) {
    if (!fiber) return;

    let fiberParent = fiber.parent;
    // 如果 fiber 的父节点没有 dom，就一直往上找，直到找到有 dom 的父节点
    if (!fiberParent.dom) {
        fiberParent = fiberParent.parent;
    }
    // 当 fiber.dom 为 null 时，说明是一个函数组件，不需要将 dom 添加到父节点上
    fiber.dom && fiberParent.dom.append(fiber.dom);

    commitWork(fiber.child);
    commitWork(fiber.sibling);
}

requestIdleCallback(workLoop);

const React = {
    createElement,
    render,
};

export default React;
